#include "config.h"

#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>

#define DBG_SUBSYS S_LIBSTORAGE

#include "sysy_lib.h"
#include "cache.h"
#include "get_version.h"
#include "cluster.h"
#include "lichstor.h"
#include "storage.h"
#include "metadata.h"
#include "configure.h"
#include "job_dock.h"
#include "chunk.h"
#include "ynet_rpc.h"
#include "net_global.h"
#include "ylog.h"
#include "dbg.h"

#define MAP_PREFIX      128
#define SHEEPDOG_MAP    SHM_ROOT"/sheepdog/map"

static atable_t atable;

static int __sd_map_load(const char *parent, const char *key, void *args)
{
        int ret, idx;
        char path[MAX_PATH_LEN], value[MAX_BUF_LEN];
        fileid_t *fileid;

        (void) args;

        if (strcmp(key, ".") == 0
            || strcmp(key, "..") == 0)
                return 0;

        snprintf(path, MAX_PATH_LEN, "%s/%s", parent, key);

        ret = _get_value(path, value, MAX_BUF_LEN);
        if (ret < 0) {
                ret = -ret;
                GOTO(err_ret, ret);
        }

        DINFO("load %s\n", key);

        YASSERT(ret == sizeof(*fileid));

        ret = ymalloc((void **)&fileid, sizeof(*fileid));
        if (unlikely(ret))
                GOTO(err_ret, ret);

        memcpy(fileid, value, sizeof(*fileid));
        idx = atoi(key);
        ret = array_table_insert(atable, idx, fileid);
        if (unlikely(ret))
                UNIMPLEMENTED(__DUMP__);

        return 0;
err_ret:
        return ret;
}



int sd_map_init()
{
        int ret;

        ret = path_validate(SHEEPDOG_MAP, YLIB_ISDIR, YLIB_DIRCREATE);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        atable = array_create_table(NULL, 32768 - MAP_PREFIX);
        if (atable == NULL) {
                ret = ENOMEM;
                GOTO(err_ret, ret);
        }

        ret = _dir_iterator(SHEEPDOG_MAP, __sd_map_load, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int sd_map_open(int *_fd, const fileid_t *fileid)
{
        int ret, fd;
        fileid_t *fid;
        char path[MAX_PATH_LEN];

        ret = ymalloc((void **)&fid, sizeof(*fid));
        if (unlikely(ret))
                GOTO(err_ret, ret);

        *fid = *fileid;

        fd = array_table_insert_empty(atable, fid);
        if (fd < 0) {
                ret = -fd;
                GOTO(err_free, ret);
        }

        DINFO("new fd %u\n", fd);

        snprintf(path, MAX_PATH_LEN, "%s/%u", SHEEPDOG_MAP, fd);
        ret = _set_value(path, (char *)fid, sizeof(*fid), O_CREAT | O_EXCL);
        if (unlikely(ret))
                GOTO(err_free, ret);

        *_fd = (fd + MAP_PREFIX);

        return 0;
err_free:
        yfree((void **)&fid);
err_ret:
        return ret;
}

int sd_map_get(int fd, fileid_t *_fid)
{
        int ret;
        fileid_t *fid;

        fid = array_table_find(atable, fd - MAP_PREFIX, NULL);
        if (fid == NULL) {
                ret = ENOENT;
                GOTO(err_ret, ret);
        }

        *_fid = *fid;

        return 0;
err_ret:
        return ret;
}

int sd_map_close(int fd)
{
        int ret;
        fileid_t *fid;
        char path[MAX_PATH_LEN];

        DINFO("close %u\n", fd - MAP_PREFIX);

        ret = array_table_remove(atable, fd - MAP_PREFIX, NULL, (void **)&fid);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        snprintf(path, MAX_PATH_LEN, "%s/%u", SHEEPDOG_MAP, fd - MAP_PREFIX);
        ret = unlink(path);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        yfree((void **)&fid);

        return 0;
err_ret:
        return ret;
}
